package com.dennytech.tac.ui.adapter;

import java.util.ArrayList;
import java.util.List;

import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;

import android.view.View;
import android.view.View.OnClickListener;
import android.view.ViewGroup;

import com.dennytech.tac.base.BasicAdapter;
import com.dennytech.tac.model.CFObject;
import com.dennytech.tac.net.BaseApi;
import com.google.android.photostream.UserTask;

public abstract class BasicLoadAdapter<T extends CFObject> extends BasicAdapter {

	protected List<T> mData = new ArrayList<T>();

	protected boolean mIsEnd;
	protected int mNextStartIndex;
	protected int mRecordCount;
	protected NetworkTask task;

	protected String mErrorMsg;
	protected String mEmptyMsg;

	public void setData(List<T> d) {
		this.mData = d;
		notifyDataSetChanged();
	}

	public void clearData() {
		if (mData != null) {
			mData.clear();
		}
		notifyDataSetChanged();
	}

	public void setErrorMsg(String msg) {
		mErrorMsg = msg;
		notifyDataSetChanged();
	}

	public void setEmptyMsg(String msg) {
		mEmptyMsg = msg;
		notifyDataSetChanged();
	}

	public void setEmpty() {
		cancelLoad();
		mData.clear();
		mNextStartIndex = 0;
		mIsEnd = true;
		mErrorMsg = null;
		mEmptyMsg = null;
		notifyDataSetChanged();
	}

	public void reset() {
		cancelLoad();
		mData.clear();
		mNextStartIndex = 0;
		mIsEnd = false;
		mErrorMsg = null;
		mEmptyMsg = null;
		notifyDataSetChanged();
	}
	
	public void cancelLoad() {
		if (task != null) {
			task.cancel(true);
		}
	}

	@Override
	public int getCount() {
		if (mIsEnd) {
			return mData.size();
		} else {
			return mData.size() + 1;
		}
	}

	@Override
	public Object getItem(int position) {
		if (position < mData.size()) {
			return mData.get(position);
		}

		return mErrorMsg == null ? LOADING : ERROR;
	}

	@Override
	public long getItemId(int position) {
		return position;
	}

	protected abstract View itemViewForData(View convertView, ViewGroup parent);

	@Override
	public View getView(int position, View convertView, ViewGroup parent) {
		Object obj = getItem(position);
		if (obj instanceof JSONObject) {
			return itemViewForData(convertView, parent);
		} else if (obj == LOADING) {
			if (mErrorMsg == null) {
				loadNewPage();
			}
			return getLoadingView(parent, convertView);
		} else if (obj == EMPTY) {
			return getEmptyView(mEmptyMsg, "The data is empty", parent, convertView);
		} else {
			View view = getFailedView(mErrorMsg, new OnClickListener() {

				@Override
				public void onClick(View v) {
					loadNewPage();
				}
			}, parent, convertView);
			return view;
		}
	}

	public abstract NetworkTask createTask();

	protected boolean loadNewPage() {
		if (mIsEnd)
			return false;
		if (task == null) {
			mErrorMsg = null;
			task = createTask();
			task.execute(mNextStartIndex);
			notifyDataSetChanged();
			return true;
		}
		return false;
	}

	protected void appendData(JSONObject list) throws JSONException {
		int nextStart = list.getInt("nextStart");
		if (nextStart == mNextStartIndex) {
			mEmptyMsg = list.getString("emptyMsg");

			JSONArray array = list.getJSONArray("list");
			for (int i = 0; i < array.length(); i++) {
				mData.add(decodeObject(array.getJSONObject(i)));
			}
			mNextStartIndex = nextStart;
			mIsEnd = Boolean.valueOf(list.getString("isEnd"));
			notifyDataSetChanged();
		} else {
			// just ignore
		}
	}

	protected abstract T decodeObject(JSONObject object);

	public static abstract class NetworkTask extends
			UserTask<Integer, Void, JSONObject> {
		protected BasicLoadAdapter<?> adapter;
		protected BaseApi api;

		public NetworkTask(BasicLoadAdapter<?> adapter) {
			this.adapter = adapter;
			this.api = creatApi();
		}
		
		protected abstract BaseApi creatApi();
		
		public BaseApi api() {
			return api;
		}

		@Override
		public synchronized void onCancelled() {
			if (api != null)
				api.abort();
			if (adapter != null)
				adapter.task = null;
		}

		@Override
		public synchronized void onPostExecute(JSONObject result) {
			if (result == null) {
				adapter.setErrorMsg(api.errorMsg());
			} else {
				try {
					adapter.appendData(result);
				} catch (JSONException e) {
					adapter.setErrorMsg("data error");
				}
			}
			adapter.task = null;
		}
	}
}
